package com.thoughtservice.jspdoc.export.argouml;

/*
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is "JSP Documentation Generator Project".
 *
 */

import java.util.*;
import java.io.*;

import com.thoughtservice.jspdoc.*;
import com.thoughtservice.jspdoc.export.Exporter;
import com.thoughtservice.jspdoc.util.*;
import com.thoughtservice.jspdoc.utils.Zipper;


/**
 * Exporter implementation.
 * Creates an ArgoUML Project file (Untitled.zargo) in the output directory.
 * This file can be imported into ArgoUML (and modified).
 *
 * @author Jochen Luell (jochen@luell.com)
 */
public class ArgoUmlExport implements Exporter {

  String outputDir = null;

  static final int NO_FROM_LINKS = -1;


  /**
   * Creates ArgoUML Project file
   *
   * @param pages ca.yourdecor.jspdoc.JSPFileList List of JSP Documents
   * @param pages java.lang.String FQN of output  directory
   * @return boolean
   */
  public boolean export(JSPFileList pages, String outputDir) {

     this.outputDir = outputDir;
     
     XmiDocument xmiDoc = new XmiDocument();
     xmiDoc.createHeader();

     ArgoDocument argoDoc = new ArgoDocument();

     Zipper zip = new Zipper(outputDir + File.separator + "Untitled.zargo");

     argoDoc.createHeader();
     

     argoDoc.addMember("pgml", "LinksTo.pgml");
     argoDoc.addMember("pgml", "FormLinkTo.pgml");
     argoDoc.addMember("pgml", "StaticIncludes.pgml");
     argoDoc.addMember("pgml", "DynamicIncludes.pgml");
     argoDoc.addMember("pgml", "RedirectsTo.pgml");
     argoDoc.addMember("xmi", "Untitled_.xmi");


     argoDoc.createFooter();
     zip.addText(argoDoc.getDocument(), "Untitled.argo");

     zip.addText(addDiagram("Links To", pages, JSPPage.LINKSTO, JSPPage.LINKSFROM, xmiDoc).getDocument(), "LinksTo.pgml");
     zip.addText(addDiagram("Form Link To", pages, JSPPage.LINKSTOFORM, NO_FROM_LINKS, xmiDoc).getDocument(), "FormLinkTo.pgml");
     zip.addText(addDiagram("Statically Includes", pages, JSPPage.S_INCLUDES, JSPPage.S_INCLUDEDIN, xmiDoc).getDocument(), "StaticIncludes.pgml");
     zip.addText(addDiagram("Dynamically Includes", pages, JSPPage.D_INCLUDES, JSPPage.D_INCLUDEDIN, xmiDoc).getDocument(), "DynamicIncludes.pgml");
     zip.addText(addDiagram("Redirects To", pages, JSPPage.REDIRECTS, NO_FROM_LINKS, xmiDoc).getDocument(), "RedirectsTo.pgml");

     xmiDoc.createFooter();
     zip.addText(xmiDoc.getDocument(), "Untitled_.xmi");
     zip.finish();


 /*    // Create zargo file
     Zipper zip = new Zipper(outputDir + File.separator + "Untitled.zargo");
     zip.addText(new ArgoDocument().getDocument(), "Untitled.argo");
     zip.addText(classDoc.getDocument(), "Untitled_classdiagram1.pgml");
     zip.addText(PgmlDocument.PGML_USECASE_DOCUMENT, "Untitled_usecasediagram1.pgml");
     zip.addText(xmiDoc.getDocument(), "Untitled_.xmi");


     zip.finish();
*/

     return true;
  }

  /**
   * Creates and PGML document for given link type and updates XMI document.
   *
   * @param  name          Name of PGML document
   * @param  pages         List of JSP pages
   * @param  linkTypeTo    Link type (To)   to include in diagram
   * @param  linkTypeFrom  Link type (From) to include in diagram
   * @param  xmiDoc        XMI document
   * @return PGML document
   */
  private PgmlDocument addDiagram(String name, JSPFileList pages, int linkTypeTo, int linkTypeFrom,
                                  XmiDocument xmiDoc) {

     Hashtable groupElements = new Hashtable();
     PgmlDocument classDoc = new PgmlDocument();

     Iterator i;

     classDoc.createHeader(name);

     // ----- In next releases these parameters should be configurable via a config file
     int xoffset = 10;
     //int xspacing = 80;
     int xspacing = 160;
     int yoffset = 40;
     //int yspacing = 80;
     int yspacing = 100;
     int count = 0;
     int width = 90;
     int height = 60;

     int elementsPerRow = 10;
     // -----
     
     // Create list of JSPS to take into considration (for given linktype)
     Hashtable validJSPs = new Hashtable();
     i = pages.fileList();
     while(i.hasNext()) {
        boolean foundLink = false;
        JSPPage page = (JSPPage) i.next();
        Iterator links = page.getConnections(linkTypeTo);

	while(links.hasNext()) {
	   foundLink = true;
	   File f = (File) links.next();
	   String rootPath = pages.getRootDir().getAbsolutePath();
	   String destName = f.getAbsolutePath().substring(rootPath.length());

	   validJSPs.put(destName.replace('\\', '/'), "valid");
	}
	
	if(foundLink) {
	   validJSPs.put(page.getNamePath().replace('\\', '/'), "valid");
	}
     }


     Hashtable currentY = new Hashtable();
     Hashtable elementsInRow = new Hashtable();

     i = pages.fileList();

     while (i.hasNext()) {
                     try {
			JSPPage p = (JSPPage) i.next();
			if(p.isPage()) {

			     // Check if page is valid
			     if(validJSPs.get(p.getNamePath().replace('\\', '/')) == null) {
			        continue;
			     }

                             String jspPath = p.getNamePath();
                             jspPath = jspPath.replace('\\', '/');

			     int level = getLevel(jspPath);

			     while(true) {
			        if(elementsInRow.get("Row" + level) == null) {
				   elementsInRow.put("Row" + level, new Integer(1));
				   break;
			         }

			        int elementCount = ((Integer) elementsInRow.get("Row" + level)).intValue();

			        if(elementCount < elementsPerRow) {
                                   elementsInRow.put("Row" + level, new Integer(++elementCount));
				   break;
				}
				else {
				   level++;
			        }
			    }

			     int y = currentY.get(new Integer(level)) != null ? ((Integer)currentY.get(new Integer(level))).intValue() : yoffset;

			     currentY.put(new Integer(level), new Integer(y + yspacing));

                             GroupElement element = new GroupElement();

			     // Add group-element
			     element.setXcoord(xoffset + level * xspacing);
			     element.setYcoord(y);
			     element.setLinkTypeTo(linkTypeTo);
			     element.setLinkTypeFrom(linkTypeFrom);
			     element.setJspName(jspPath);
			     element.setDescription(p.getPageDesc());
			     element.setLevel(level);
			     element.setGroupName("Fig" + count);
			     // First element in XMI file starts with 2
			     element.setXmiName("xmi." + (count + 2) + "-" + linkTypeTo);
			     element.setWidth(width);
			     element.setHeight(height);
			     element.setJspPage(p);

			     groupElements.put(jspPath, element);

			     classDoc.addGroup(element);
			     xmiDoc.addClass(element, pages);

			     count++;

			}
                     } catch(Exception e) { e.printStackTrace(); }
     }

     // Create dependencies
     i = pages.fileList();

     while (i.hasNext()) {
                     try {
			JSPPage p = (JSPPage) i.next();

			if(validJSPs.get(p.getNamePath().replace('\\', '/')) != null) {
                           Iterator linksTo = p.getConnections(linkTypeTo);

			   while(linksTo.hasNext()) {
			        File f = (File) linksTo.next();
				String rootPath = pages.getRootDir().getAbsolutePath();
				//String destName = f.getAbsoluteFile().substring(rootPath.length());
				String destName = f.getPath().substring(rootPath.length());
				destName = absolutePath(destName);
				String sourceName = p.getNamePath();
				sourceName = sourceName.replace('\\', '/');

				GroupElement elementSource = (GroupElement) groupElements.get(sourceName);
				GroupElement elementDest = (GroupElement) groupElements.get(destName);

				if(elementSource == null || elementDest == null) {

                                   if(elementDest == null) {
                                        System.out.println("WARNING: Destination " + destName +
                                                           " does not exist (link in " + sourceName + ")");
                                   }

				   xmiDoc.addEmptyDependency(sourceName, destName, linkTypeTo);
				   continue;
				}

				classDoc.addDependency(elementSource, elementDest, count);
				xmiDoc.addDependency(elementSource, elementDest);

				count++;
		           }

		        }
                     } catch(Exception e) { e.printStackTrace(); }

     }

     classDoc.createFooter();

     return classDoc;
  }


  /**
   * Get JSP hierarchie level (starting from 0)
   *
   * @param in java.lang.String Path of JSP page relative to root directory
   * @return int Level
   */
  private int getLevel(String in) {

     int res = 0;
     for(int i = 0; i < in.length(); i++) {
         if(in.charAt(i) == '/') {
            res++;
         }
     }

     return (res - 1);
  }
  
  /**
   * Get absolute path name
   *
   * @param name File name
   * @ return Absolute path name
   */
   private String absolutePath(String name) {
      name = name.replace('\\', '/');
      
      if(!name.startsWith("/")) {
         name = "/" + name;
      }

      try {
         RE re;
	 
	 re = new RE("\\/\\.\\/");
	 name = re.subst(name, "\\/");

         while(name.indexOf("/../") != -1) {
	    re = new RE("\\/[^\\/]+\\/\\.\\.");
	    name = re.subst(name, "");

	 }
      }
      catch(Exception e) {
         e.printStackTrace();
      }

      return name;
   }

}
